package com.wb.component.event

import com.fasterxml.jackson.databind.ObjectMapper
import com.google.gson.Gson
import com.wb.component.event.MsgSend.Companion.toHexString
import com.wb.utils.RestUtil
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.http.converter.StringHttpMessageConverter
import org.springframework.stereotype.Component
import org.springframework.util.LinkedMultiValueMap
import org.springframework.util.MultiValueMap
import org.springframework.web.client.RestTemplate
import java.net.URLDecoder
import java.net.URLEncoder
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.util.regex.Pattern
import javax.annotation.Resource

/**
 * 消息发送
 *
 * @author 祁雪
 */
@Component
open class MsgSend {

    @Resource
    open fun setRest(restTemplate: RestTemplate) {
        MsgSend.restTemplate = restTemplate
    }

    @Value("\${message.send.url}")
    open fun setSendUrl(url: String) {
        MsgSend.sendUrl = url
    }

    @Value("\${bot.wx.id}")
    open fun setBotWXID(wxid: String) {
        MsgSend.botWXID = wxid
    }

    companion object {

        val headerMap = hashMapOf<String, String>(
            "Content-Type" to "application/json;charset=UTF-8"
        )
        lateinit var sendUrl: String

        lateinit var botWXID: String

        lateinit var restTemplate: RestTemplate

        /**
         * 发送文本消息
         */
        fun sendText(toWXID: String?, msg: String): HashMap<String, Any>? {
            if (toWXID.isNullOrBlank()) {
                return null
            }
            val map = hashMapOf<String, Any>(
                "event" to "SendTextMsg",
                "robot_wxid" to botWXID,
                "to_wxid" to toWXID,
//                "msg" to encode(msg)
                "msg" to msg
            )

            return sendMessage(map)
        }


        fun sendImage(
            toWXID: String?, name: String?,
            url: String, path: String
        ): HashMap<String, Any>? {
            return sendFileMessage(toWXID, name, url, path, "image")
        }

        fun sendVideo(
            toWXID: String?, name: String?,
            url: String, path: String
        ): HashMap<String, Any>? {
            return sendFileMessage(toWXID, name, url, path, "video")
        }

        fun sendEmoji(
            toWXID: String?, name: String?,
            url: String, path: String
        ): HashMap<String, Any>? {
            return sendFileMessage(toWXID, name, url, path, "emoji")
        }

        fun sendFile(
            toWXID: String?, name: String?,
            url: String, path: String
        ): HashMap<String, Any>? {
            return sendFileMessage(toWXID, name, url, path, null)
        }

        fun sendLink(
            toWXID: String?, title: String,
            text: String?, targetUrl: String, picUrl: String?,
            iconUrl: String?
        ): HashMap<String, Any>? {

            if (toWXID.isNullOrBlank()) {
                return null
            }

            val map = hashMapOf<String, Any>(
                "event" to "SendLinkMsg",
                "robot_wxid" to botWXID,
                "to_wxid" to toWXID,
                "msg" to mapOf(
                    "title" to title,
                    "text" to text,
                    "target_url" to targetUrl,
                    "pic_url" to picUrl,
                    "icon_url" to iconUrl
                )
            )

            return sendMessage(map)

        }

        private fun sendFileMessage(
            toWXID: String?, name: String?,
            url: String, path: String,
            type: String?
        ): HashMap<String, Any>? {
            val eventType = if (type.isNullOrBlank()) "SendFileMsg" else {
                when (type) {
                    "image" -> {
                        if (url.endsWith(".gif", true) || path.endsWith(".gif", true)) {
                            "SendEmojiMsg"
                        } else {
                            "SendImageMsg"
                        }
                    }
                    "video" -> "SendVideoMsg"
                    "emoji" -> "SendEmojiMsg"
                    else -> "SendFileMsg"
                }
            }
            if (toWXID.isNullOrBlank()) {
                return null
            }
            val map = hashMapOf<String, Any>(
                "event" to eventType,
                "robot_wxid" to botWXID,
                "to_wxid" to toWXID,
                "msg" to mapOf(
                    "name" to name,
//                    "url" to encodeToUrl(url),
                    "url" to url,
                    "path" to path,
                    "patch" to path
                )
            )

            return sendMessage(map)
        }

        fun sendMessage(map: HashMap<String, Any>): HashMap<String, Any>? {

            val result = post(sendUrl, map, hashMapOf<String, Any>().javaClass)

            println("消息回调 => ${result}")

            return result
        }

        private fun <T> post(
            url: String,
            param: HashMap<String, Any>,
            clazz: Class<T>
        ): T? {

            val rest = restTemplate

            val header = HttpHeaders()

//            header.contentType = MediaType.APPLICATION_JSON
            header.contentType = MediaType.parseMediaType("application/json; charset=utf-8")
            header.add("Accept", MediaType.APPLICATION_JSON.toString());


            val json = when (param.getOrDefault("event", "")) {
                "SendImageMsg" -> ObjectMapper().writeValueAsString(param)
                "SendVideoMsg" -> ObjectMapper().writeValueAsString(param)
                "SendEmojiMsg" -> ObjectMapper().writeValueAsString(param)
                "SendFileMsg" -> ObjectMapper().writeValueAsString(param)
                else -> {
                    encode(ObjectMapper().writeValueAsString(param))
                }
            }

            println("消息发送 => ${json}")

            val entity = HttpEntity<String>(json, header)

            val result = rest.postForObject(url, entity, clazz)

            return result
        }

        private fun encodeToUrl(url: String?): String {
            if (url.isNullOrBlank()) {
                return ""
            }
            return url.toCharArray()
                .map { char ->
                    val ub = Character.UnicodeBlock.of(char)
                    if (char.toString().matches(Regex("[\\u4e00-\\u9fa5]"))
                        || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                        || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                        || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                        || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
                        || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                        || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
                        || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
                    ) {
                        return@map URLEncoder.encode(char.toString(), Charset.defaultCharset())
                    }
                    return@map char.toString()
                }.joinToString("")
        }

        private fun encode(text: String) = text
            .toCharArray()
            .map { encode(it) }
            .joinToString(separator = "", truncated = "")

        private fun encode(char: Char): String {
            val ub = Character.UnicodeBlock.of(char)
            if (char.toString().matches(Regex("[\\u4e00-\\u9fa5]"))
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
            ) {
                return "\\u${char.toInt().toHexString()}"
            }
            return char.toString()
        }

        private fun Int.toHexString(): String = Integer.toHexString(this)
    }

}